/*
 * @Author: Clark
 * @Email: haixuanwoTxh@gmail.com
 * @Date: 2021-12-10 12:08:19
 * @LastEditors: Clark
 * @LastEditTime: 2021-12-11 19:42:15
 * @Description: file content
 */

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
}

#include "tcp_socket.h"

TcpServer::TcpServer(unsigned short port)
{
    this->port = port;
    serverFd = -1;
    protocolEngine = new ProtocolEngine;
}

/**
 * @brief 初始化socket服务
 *        实现创建socket、bind、listen、accept，并收发数据
 * @return true 成功
 * @return false 失败
 */
bool TcpServer::init()
{
    int ret = -1;
    struct sockaddr_in serverAddr;

    // 创建套接字
    serverFd = socket(AF_INET, SOCK_STREAM, 0);
    if (serverFd < 0)
    {
        perror("socket fail");
        return false;
    }

    // 绑定
    bzero(&serverAddr, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(port);
    ret = bind(serverFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    if (ret < 0)
    {
        perror("bind fail");
        return false;
    }

    // 监听
    ret = listen(serverFd, CLIENT_SIZE); /* 默认最大128 */
    if (ret < 0)
    {
        perror("bind fail");
        return false;
    }

    return true;
}

/**
 * @brief 开始服务
 *      等待客户端的连接，接收已连接客户端的数据
 * @return true 成功
 * @return false 失败
 */
bool TcpServer::start()
{
    int eventCount = 0;

    //存放有事件发生的结构数组
    struct epoll_event events[MAX_EVENTS];

    // epoll创建一个描述符
    // Since Linux 2.6.8, the size argument is ignored, but must be greater than zero
    epollFd = epoll_create(1);

    // 添加监听服务端描述符事件
    struct epoll_event epollEvent;
    epollEvent.data.fd = serverFd;
    epollEvent.events = EPOLLIN;
    epoll_ctl(epollFd, EPOLL_CTL_ADD, serverFd, &epollEvent);

    printf("server start success listen:%u\n", port);

    while (1)
    {
        //等待监视的socket有事件发生
        eventCount = epoll_wait(epollFd, events, MAX_EVENTS, NEVER_TIMEOUT);
        if (eventCount < 0)
        {
            printf("epoll_wait() failed.\n");
            close(epollFd);
            return false;
        }
        else if (0 == eventCount)
        {
            printf("epoll_wait() timeout.\n");
            continue;
        }

        for (int i = 0; i < eventCount; i++)
        {
            if (events[i].events & EPOLLIN)
            {
                if (events[i].data.fd == serverFd)
                {
                    deal_accept();
                }
                else
                {
                    deal_receive_data(events[i].data.fd);
                }
            }
        }
    }

    return true;
}

TcpServer::~TcpServer()
{
    close(serverFd);
    delete protocolEngine;
}

/**
 * @brief accept连接的客户端
 * @return true 成功
 * @return false 失败
 */
bool TcpServer::deal_accept()
{
    // 如果发生事件的是ListenSock，表示有新的客户端连上来。
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    int clientFd = accept(serverFd, (struct sockaddr*)&client, &len);
    if (clientFd < 0)
    {
        perror("accept() failed.\n");
        return false;
    }

    // 把新的客户端添加到epoll中。
    struct epoll_event epollEvent;
    memset(&epollEvent, 0, sizeof(struct epoll_event));
    epollEvent.data.fd = clientFd;
    epollEvent.events = EPOLLIN;
    epoll_ctl(epollFd, EPOLL_CTL_ADD, clientFd, &epollEvent);

    printf("Welcom client fd[%d] from %s:%u\n", clientFd, inet_ntoa(client.sin_addr), client.sin_port);
    return true;
}

/**
 * @brief 处理收到的客户端数据
 * @param clientFd 与客户端对应的socket fd
 * @return true 成功
 * @return false 失败
 */
bool TcpServer::deal_receive_data(int clientFd)
{
    // 客户端有数据过来或客户端的socket连接被断开。
    unsigned char Buffer[1024];
    memset(Buffer, 0, sizeof(Buffer));

    // 读取客户端的数据。
    ssize_t readLen = read(clientFd, Buffer, sizeof(Buffer));

    // 发生了错误或socket被对方关闭。
    if (readLen <=0)
    {
        disconnet_client(clientFd);
        protocolEngine->disconnect(clientFd);
        return false;
    }

    printf("Receive from client fd[%d] readLen[%ld]:%s\n", clientFd, readLen, Buffer);

    //把收到的报文发回给客户端。
    // write(clientFd, Buffer, strlen(Buffer));
    protocolEngine->parse_packet_data(Buffer, readLen, clientFd);
    return true;
}

/**
 * @brief 断开客户端的连接
 * @param clientFd 与客户端对应的socket fd
 * @return true 成功
 * @return false 失败
 */
bool TcpServer::disconnet_client(int clientFd)
{
    struct epoll_event epollEvent;
    // 把已断开的客户端从epoll中删除。
    memset(&epollEvent,0,sizeof(struct epoll_event));
    epollEvent.events = EPOLLIN;
    epollEvent.data.fd = clientFd;
    epoll_ctl(epollFd, EPOLL_CTL_DEL, clientFd, &epollEvent);

    close(clientFd);

    printf("Client fd[%d] disconnected.\n", clientFd);
    return true;
}

/**
 * @brief 设置服务端为非阻塞
 * @return true 成功
 */
bool TcpServer::setNonblock()
{
    int mode = 0;

    mode = fcntl(serverFd, F_GETFL);
    mode |= O_NONBLOCK;
    fcntl(serverFd, F_SETFL, mode);

    return true;
}
